
#ifndef _INTERLEAVED_BODIES_
#define _INTERLEAVED_BODIES_


//-------------------------------------------------------------------------------------
// This file contains implementation of bodies with interleaved coordinates: each body
// is defined as an index of arrays with coordinates. This version can be used for SSE
// and CUDA optimizations.
//-------------------------------------------------------------------------------------

#include <vector>
#include "InteractionLogic.h"

using namespace ttg::math;

//Array of vect3, that coordinated is interleaved.
//Can be useful for some optimizations (e.g. CUDA coalescing or SSE).
template <class T>
class interleaved_vec3_array
{
	private:
		std::vector<T> x, y, z;
	public:
		//Creates array with requred size.
		inline interleaved_vec3_array(size_t size = 0)
		{ resize(size); }
		//Returns x-coordinate of required element.
		T &getX(size_t n)
		{ return x[n]; }
		//Returns x-coordinates as C array.
		T *getX()
		{ return &x[0]; }
		//Returns y-coordinate of required element.
		T &getY(size_t n)
		{ return y[n]; }
		//Returns y-coordinates as C array.
		T *getY()
		{ return &y[0]; }
		//Returns z-coordinate of required element.
		T &getZ(size_t n)
		{ return z[n]; }
		//Returns z-coordinates as C array.
		T *getZ()
		{ return &z[0]; }
		//Returns size of array.
		inline size_t getSize()
		{ return x.size(); }
		//Resizes array.
		inline void resize(size_t new_size)
		{
			x.resize(new_size);
			y.resize(new_size);
			z.resize(new_size);
		}
};

typedef interleaved_vec3_array<float> interleaved_vec3f_array;

//Set of bodies with interleaved coordinates.
class InterleavedBodies
{
	public:
		interleaved_vec3f_array positions;
		interleaved_vec3f_array velocities;
		interleaved_vec3f_array accelerations;
		std::vector<float> masses;
	public:
		//Resizes current body set.
		inline void resize(size_t new_size)
		{
			positions.resize(new_size);
			velocities.resize(new_size);
			accelerations.resize(new_size);
			masses.resize(new_size);
		}
		//Returns count of bodies.
		inline size_t getSize()
		{ return positions.getSize(); }
	public:
		//SubVector of bodies. Will be used for unified access.
		struct SubVector
		{
			private:
				InterleavedBodies *owner;
				size_t lo, hi;							//lower and higher bounds
				int tag;								//user tag
			public:
				//Creates empty vector.
				inline SubVector()
				{
					this->owner = NULL;
					this->lo = 0;
					this->hi = 0;
					this->tag = 0;
				}
				//Creates vector with all bodies.
				inline SubVector(InterleavedBodies *owner)
				{
					this->owner = owner;
					this->lo = 0;
					this->hi = owner->positions.getSize();
					this->tag = 0;
				}
				//Creates vector with required range of bodies.
				inline SubVector(SubVector &sv, size_t lo, size_t hi)
				{
					this->owner = sv.owner;
					this->lo = sv.lo + lo;
					this->hi = sv.lo + hi;
					this->tag = sv.tag;
				}
				//Returns tag, that was assigned by user.
				inline int getTag()
				{ return tag; }
				//Sets user's tag.
				inline void setTag(int tag)
				{ this->tag = tag; }
				//Returns size of current vector.
				inline size_t getSize() const
				{ return hi - lo; }
				//Returns real index of n-th element.
				inline size_t getRealIndex(size_t n)
				{ return n + lo; }
				//Returns position of i-th body.
				//Note: it will be casted.
				inline vec3f getPosition(size_t i)
				{
					size_t ri = getRealIndex(i);
					return vec3f(owner->positions.getX(ri),
								 owner->positions.getY(ri),
								 owner->positions.getZ(ri));
				}
				//Returns owner of this sub-vector.
				inline InterleavedBodies *getOwner()
				{ return owner; }
				//Returns SubVector, that wraps all bodies.
				inline SubVector getOwnerSubVector()
				{ return SubVector(getOwner()); }
				//Interacts two bodies("b1" with "b2") of this subvector.
				//Updates velocity of b1, but does not update "b2".
				inline void interactWith(size_t b1, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = getRealIndex(b2);
					if (rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWith(//first body
												   owner->positions.getX(rb1),
												   owner->positions.getY(rb1),
												   owner->positions.getZ(rb1),
												   owner->accelerations.getX(rb1),
												   owner->accelerations.getY(rb1),
												   owner->accelerations.getZ(rb1),
												   owner->masses[rb1],
												   //second body
												   owner->positions.getX(rb2),
												   owner->positions.getY(rb2),
												   owner->positions.getZ(rb2),
												   owner->masses[rb2]);
				}
				//Interacts two bodies("b1" with "b2") of diffrent subvectors.
				//Updates velocity of b1, but does not update "b2".
				inline void interactWith(size_t b1, SubVector &v2, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = v2.getRealIndex(b2);
					if (getOwner() == v2.getOwner() && rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWith(//first body
												   owner->positions.getX(rb1),
												   owner->positions.getY(rb1),
												   owner->positions.getZ(rb1),
												   owner->accelerations.getX(rb1),
												   owner->accelerations.getY(rb1),
												   owner->accelerations.getZ(rb1),
												   owner->masses[rb1],
												   //second body
												   v2.getOwner()->positions.getX(rb2),
												   v2.getOwner()->positions.getY(rb2),
												   v2.getOwner()->positions.getZ(rb2),
												   v2.getOwner()->masses[rb2]);
				}
				//Interacts requred bodies.
				//Updates accelerations of "b1" and "b2"
				inline void interactWithEachOther(size_t b1, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = getRealIndex(b2);
					if (rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWithEachOther(
												   //first body
												   owner->positions.getX(rb1),
												   owner->positions.getY(rb1),
												   owner->positions.getZ(rb1),
												   owner->accelerations.getX(rb1),
												   owner->accelerations.getY(rb1),
												   owner->accelerations.getZ(rb1),
												   owner->masses[rb1],
												   //second body
												   owner->positions.getX(rb2),
												   owner->positions.getY(rb2),
												   owner->positions.getZ(rb2),
												   owner->accelerations.getX(rb2),
												   owner->accelerations.getY(rb2),
												   owner->accelerations.getZ(rb2),
												   owner->masses[rb2]);
				}
				//Interacts requred bodies.
				//Updates accelerations of "b1" and "b2"
				inline void interactWithEachOther(size_t b1, SubVector &sv2, size_t b2)
				{
					//Getting real indices.
					size_t rb1 = getRealIndex(b1);
					size_t rb2 = sv2.getRealIndex(b2);
					if (getOwner() == sv2.getOwner() && rb1 == rb2)
						return;
					//Interacting.
					InteractionLogic::interactWithEachOther(
												   //first body
												   owner->positions.getX(rb1),
												   owner->positions.getY(rb1),
												   owner->positions.getZ(rb1),
												   owner->accelerations.getX(rb1),
												   owner->accelerations.getY(rb1),
												   owner->accelerations.getZ(rb1),
												   owner->masses[rb1],
												   //second body
												   sv2.getOwner()->positions.getX(rb2),
												   sv2.getOwner()->positions.getY(rb2),
												   sv2.getOwner()->positions.getZ(rb2),
												   sv2.getOwner()->accelerations.getX(rb2),
												   sv2.getOwner()->accelerations.getY(rb2),
												   sv2.getOwner()->accelerations.getZ(rb2),
												   sv2.getOwner()->masses[rb2]);
				}
				//Updates position and velocity of required body
				//and zeroes its acceleration.
				inline void update(size_t b, float dt)
				{
					size_t rb = getRealIndex(b);
					InteractionLogic::update(owner->positions.getX(rb),
											 owner->positions.getY(rb),
											 owner->positions.getZ(rb),
											 owner->velocities.getX(rb),
											 owner->velocities.getY(rb),
											 owner->velocities.getZ(rb),
											 owner->accelerations.getX(rb),
											 owner->accelerations.getY(rb),
											 owner->accelerations.getZ(rb),
											 dt);
				}
		};
};

#endif
